home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CU Amiga Super CD-ROM 17
/
CU Amiga Magazine's Super CD-ROM 17 (1997)(EMAP Images)(GB)[!][issue 1997-12].iso
/
CUCD
/
Programming
/
DiceSource
/
src
/
vopts
/
command.c
next >
Wrap
C/C++ Source or Header
|
1997-09-09
|
30KB
|
1,007 lines
/*
* (c)Copyright 1992-1997 Obvious Implementations Corp. Redistribution and
* use is allowed under the terms of the DICE-LICENSE FILE,
* DICE-LICENSE.TXT.
*/
#include "vopts.h"
Prototype void do_command(char *string);
Prototype void read_file(int type);
Prototype void save_file(int type);
Prototype int get_filename(char *str);
Prototype void reset_options(int reshow);
Prototype void reset_group(struct G_OBJECT *object);
Prototype int match_opt(char *optstr,char **valstr,char *buf, int exact);
Prototype int set_option(struct G_OBJECT *object,char **argv, int exact);
Prototype int parse_options(char *string);
Prototype int get_option(struct G_OBJECT *object, char **buf, int *len);
Prototype int append_option(char *optstr1, char *optstr2, char **buf, int *len);
parse_options(char *string);
/***********************************************************************************
* Procedure: do_command
* Synopsis: do_command(string);
* Purpose: Execute the command associated with a given string
***********************************************************************************/
void do_command(char *string)
{
char *p;
char buf[10];
int len;
int type;
/* The following valid commands are allowed: */
/* READ [ file | ? | ENV ] */
/* SAVE [ file | ? | ENV ] */
/* RESET */
/* QUIT */
/* PARSE option */
len = 0;
while (*string == ' ') string++;
p = string;
while(((*p >= 'A') && (*p <= 'Z')) ||
((*p >= 'a') && (*p <= 'z')))
{
if (*p < 'Z') /* Upper case characters which occur first in ASCII */
buf[len++] = *p;
else
buf[len++] = 'A' + (*p - 'a');
if (len > 5) break;
p++;
}
buf[len] = 0;
while(*p == ' ') p++;
if (!strcmp(buf, "READ"))
{
global.fileop = 0;
type = get_filename(p);
if (type == 0) /* user aborted filename */
return;
SetWindowTitles(global.window, global.title, global.wtitle);
global.filetype = type;
read_file(type);
}
else if (!strcmp(buf, "SAVE"))
{
global.fileop = 1;
type = get_filename(p);
if (type == 0) /* user aborted filename */
return;
if (request(0, TEXT_SAVEOK, global.filename, NULL) == 0)
/* Chickened out... */
return;
SetWindowTitles(global.window, global.title, global.wtitle);
global.filetype = type;
save_file(type);
}
else if (!strcmp(buf, "QUIT"))
{
global.done = 1;
}
else if (!strcmp(buf, "PARSE"))
{
char tbuf[MAX_STRING+1];
strncpy(tbuf, p, MAX_STRING);
set_gadgets(0);
parse_options(tbuf);
set_gadgets(1);
}
else if (!strcmp(buf, "RESET"))
{
reset_options(1);
}
else
{
/* We have an invalid option, ignore the command and let them know */
/* about the problem with the option. */
request(1, TEXT_BADCMD, buf, NULL);
return;
}
}
/***********************************************************************************
* Procedure: read_file
* Synopsis: read_file(type)
* Purpose: read file, extract options according to (type) and display
***********************************************************************************/
void read_file(int type)
{
char buf[256];
if (type == FILE_ENV)
{
strncpy(buf, getenv("DCCOPTS"), 255);
reset_options(0);
parse_options(buf);
}
else
{
FILE *fp;
int cstate;
fp = fopen(global.filename, "r");
if (fp == NULL)
{
request(1, TEXT_BADFILE, global.filename, NULL);
return;
}
reset_options(0);
cstate = 1;
while(cstate && (fgets(buf, 256, fp) != NULL))
{
int len;
int extend;
len = strlen(buf)-1;
if (len < 2) continue;
buf[len] = 0; /* Wipe out the \n */
extend = 0;
if (buf[len-1] == '\\')
{
extend = 1;
buf[--len] = 0;
}
switch(type)
{
case FILE_C:
/* Make sure it says #pragma DCCOPTS */
if (!memcmp(buf, "#pragma DCCOPTS ", 16))
{
/* We actually got it */
if (cstate == 3)
{
request(1, TEXT_EXTRAPRG, NULL, NULL);
cstate = 0;
}
else
{
if (parse_options(buf+16))
cstate = 0;
else
cstate = 2;
}
}
else
{
if (cstate == 2) cstate = 3;
}
break;
case FILE_OPTIONS:
cstate = !parse_options(buf);
break;
case FILE_DMAKEFILE:
if ((!memcmp(buf, "CFLAGS=", len=7)) ||
(len = 0, cstate == 2))
{
cstate = 0;
if (extend) cstate = 2;
if (parse_options(buf+len))
cstate = 0;
}
break;
}
}
global.nameoffile = 1; /* indicate that global.filename has been read in */
fclose(fp);
}
set_gadgets(1);
}
/***********************************************************************************
* Procedure: save_file
* Synopsis: save_file(type)
* Purpose: Write user selected options to file, formatted for type
***********************************************************************************/
void save_file(int type)
{
#define MAXOPTSTR 512
char buf[MAXOPTSTR];
char *pbuf=buf;
struct G_GROUP *group;
int ok, len = MAXOPTSTR - 1;
int skipping = 0;
*buf = 0; /* just in case no options are set */
if (ok = get_option(global.objects, &pbuf, &len)) /* didn't over run buffer...*/
for (group = global.groups;
ok && (group != NULL);
group = (struct G_GROUP *)group->base.next)
{
if (!((type == FILE_ENV) && group->local))
ok = get_option(group->objects, &pbuf, &len);
}
if (!ok)
{
ok = request(0, TEXT_BIGOPTS, NULL, NULL);
if (!ok) return;
}
#ifdef JGM_DBG
printf("Options: %s\n", buf);
#endif
if (*buf == 0) return; /* let's not try anything fancy */
if (type == FILE_ENV)
{
setenv("DCCOPTS", buf);
}
else
{
FILE *fpout, *fpin;
if (global.nameoffile != 1)
{
/* didn't already read this file - does it exist? */
fpin = fopen(global.filename, "r");
/* yes I know lock is more efficient ... */
if (fpin != NULL)
{
fclose(fpin); /* it don't have nothing we want */
ok = request(0, TEXT_OVWRITEOK, global.filename, NULL);
if (!ok) return;
}
}
switch (type)
{
case FILE_OPTIONS:
{
char *p1 = buf, *p2;
fpout = fopen(global.filename, "w");
if (fpout == NULL)
{
request(1, TEXT_BADFILE, global.filename, NULL);
return;
}
do
{
p2 = strchr(p1+1, '-');
if (p2 != NULL)
p2[-1] = '\0';
fputs(p1, fpout);
fputc('\n', fpout);
p1 = p2;
}
while (p1 != NULL);
fclose(fpout);
break;
}
case FILE_DMAKEFILE:
{
int werr = 1; /* init to no error */
char newname[MAX_FILENAME+11];
char copybuf[256];
strcpy(newname, global.filename);
strcat(newname, ".vopt-temp");
if ((fpin = fopen(newname, "r")) != NULL)
{
fclose(fpin);
remove(newname);
}
if (rename(global.filename, newname) < 0) /* error */
{
request(1, TEXT_NORENAME, global.filename, NULL);
return;
}
if ((fpin = fopen(newname, "r")) == NULL)
{
rename(newname, global.filename);
request(1, TEXT_NOREAD, global.filename, NULL);
return;
}
if ((fpout = fopen(global.filename, "w")) == NULL)
{
fclose(fpin);
rename(newname, global.filename);
request(1, TEXT_BADFILE, global.filename, NULL);
return;
}
/* note that both fprintf() and fputs() return <0 for error */
while ((fgets(copybuf, 256, fpin) != NULL) && (werr >= 0))
{
if (!memcmp(copybuf, "CFLAGS=", 7))
{
werr = fprintf(fpout, "%s%s\n", "CFLAGS= ", buf);
skipping = 1; /* might not turn out to be true... */
}
else
if (!skipping)
werr = fputs(copybuf, fpout);
if (skipping)
{
len = strlen(copybuf);
if (copybuf[len-2] != '\\')
skipping = 0; /* finished (if we ever started) */
}
}
fclose(fpout);
fclose(fpin);
if (werr < 0) /* error writing file - don't rename */
{
request(1, TEXT_NOWRITE, newname, NULL);
remove(global.filename);
rename(newname, global.filename);
return;
}
remove(newname); /*delete backup copy of file */
break;
}
case FILE_C:
{
int werr = 1; /* init to no error */
int cstate = 0; /* -1 = #pragma DCCOPTS
0 = ordinary line
1 = comment continuation
*/
char newname[MAX_FILENAME+11];
char copybuf[256];
strcpy(newname, global.filename);
strcat(newname, ".vopt-temp");
if ((fpin = fopen(newname, "r")) != NULL)
{
fclose(fpin);
remove(newname);
}
if (rename(global.filename, newname) < 0) /* error */
{
request(1, TEXT_NORENAME, global.filename, NULL);
return;
}
if ((fpin = fopen(newname, "r")) == NULL)
{
rename(newname, global.filename);
request(1, TEXT_NOOVWRITE, global.filename, NULL);
return;
}
if ((fpout = fopen(global.filename, "w")) == NULL)
{
fclose(fpin);
rename(newname, global.filename);
request(1, TEXT_BADFILE, global.filename, NULL);
return;
}
werr = fprintf(fpout, "%s%s\n", "#pragma DCCOPTS ", buf);
while ((fgets(copybuf, 256, fpin) != NULL) && (werr >= 0))
{
if (cstate == 0)
{
if (!memcmp(copybuf, "#pragma DCCOPTS ", 16))
cstate = -1;
else
;
}
/* else check for close comment */
if (cstate >= 0)
werr = fputs(copybuf, fpout);
else
cstate = 0; /* next line is ordinary - not comment */
}
fclose(fpout);
fclose(fpin);
if (werr < 0) /* error writing file - don't rename */
{
request(1, TEXT_NOOVWRITE, newname, NULL);
remove(global.filename);
rename(newname, global.filename);
return;
}
remove(newname); /*delete backup copy of file */
break;
}
default:
printf("unimplemented file type %d\n", type);
}
}
}
/***********************************************************************************
* Procedure: get_filename
* Synopsis: type = get_filename(str)
* Purpose: Parse out a file descriptor and return a type for that name
***********************************************************************************/
int get_filename(char *str)
{
char *p;
int len;
if (*str)
{
/* They actually specified a name. All we need to do is replace the */
/* current one. If they specified a '?', then we need to prompt the */
/* user with the file requester. We will assume the ASL one for now */
/* but should have a reasonable fall back one. */
if (!strcmp(str, "?"))
{
if (global.freq) /* could be ASL or Arp */
{
int n;
if (AslBase != NULL)
{
/*
struct TagItem taglist[2];
*/
struct TagItem taglist[5];
#if 0
taglist[0].ti_Tag = ASL_Hail;
taglist[0].ti_Data = (ULONG)(v+1);
#endif
/* new */
/* Pattern to match against project files */
taglist[0].ti_Tag = ASL_Pattern;
taglist[0].ti_Data = (ULONG)"(#?FILE|#?DM#AK#?|#?.DICE)";
taglist[1].ti_Tag = ASL_File;
taglist[1].ti_Data = (ULONG)"";
taglist[2].ti_Tag = ASL_Dir;
taglist[2].ti_Data = (ULONG)"";
taglist[3].ti_Tag = TAG_DONE;
if (!AslRequest( (APTR)global.freq, taglist))
return(0);
}
else /* must be Arp since we have a requester... */
{
if (!ArpFileRequest(global.freq))
return (0);
}
n = strlen(global.freq->rf_Dir);
if (n > 255) n = 255;
p = global.filename;
memcpy(p, global.freq->rf_Dir, n);
p += n;
if ((n > 0) && (p[-1] != ':') && (p[-1] != '/'))
{
*p++ = '/';
n++;
}
len = strlen(global.freq->rf_File);
if ((n + len) > 255) len = 255-n;
strncpy(p, global.freq->rf_File, len);
p[len] = 0;
global.nameoffile = 0; /* new name - possibly unsafe to write */
}
else
{
/* We don't have ASL.LIBRARY, try something else instead */
set_fr_gadgets(global.filename);
/* If user hits save button, we'll be back with the name */
return 0;
}
}
else
{
strncpy(global.filename, str, MAX_FILENAME);
}
}
if (!stricmp(global.filename, "ENV"))
{
strcpy(global.title, global.text[TEXT_GLOBOPTS]);
return(FILE_ENV);
}
len = strlen(global.filename);
p = global.filename + strlen(global.filename);
strcpy(global.title, global.text[TEXT_FILEOPTS]);
strncat(global.title, global.filename, MAX_TITLE);
global.title[MAX_TITLE] = 0;
if (len < 2)
return(FILE_OPTIONS);
if ((len >= 7) && (!stricmp(p-7, "DCCOPTS")))
return(FILE_OPTIONS);
if ((len >= 9) && (!stricmp(p-9, "DMAKEFILE")))
return(FILE_DMAKEFILE);
if ((len >= 5) && (!stricmp(p-5, ".DICE")))
return(FILE_DMAKEFILE);
if (!stricmp(p-2, ".H"))
return(FILE_C);
if (!stricmp(p-2, ".C"))
return(FILE_C);
return(FILE_OPTIONS);
}
/***********************************************************************************
* Procedure: reset_options
* Synopsis: reset_options(reshow)
* Purpose: Reset all the options to the default values
***********************************************************************************/
void reset_options(int reshow)
{
struct G_GROUP *group;
set_gadgets(0);
reset_group(global.objects);
for(group = global.groups; group != NULL;
group = (struct G_GROUP *)group->base.next)
{
reset_group(group->objects);
}
if (reshow) set_gadgets(1);
}
/***********************************************************************************
* Procedure: reset_group
* Synopsis: reset_group(object)
* Purpose: Reset all the options in a group to the default values
***********************************************************************************/
void reset_group(struct G_OBJECT *object)
{
#define object_check ((struct G_CHECK *)object)
#define object_list ((struct G_LIST *)object)
#define object_str ((struct G_STRING *)object)
#define object_cycle ((struct G_CYCLE *)object)
while(object != NULL)
{
switch(object->class)
{
case CLASS_STRING:
object_str->buf[0] = 0;
break;
case CLASS_CYCLE:
{
struct G_VALUE *val;
val = object_cycle->curval = object_cycle->values;
while(val != NULL)
{
if (val->string)
val->string->buf[0] = 0;
val = val->next;
}
}
break;
case CLASS_LIST:
{
struct G_ENTRY *ent;
ent = object_list->first;
object_list->first = object_list->top = NULL;
object_list->string = object_list->maxent = 0;
while(ent != NULL)
{
struct G_ENTRY *nextent;
nextent = (struct G_ENTRY *)ent->base.next;
free_mem(ent, sizeof(struct G_ENTRY));
ent = nextent;
}
}
break;
case CLASS_CHECK:
object->state = 0;
if (!object_check->option1[0]) object->state = 1;
break;
default:
object->state = 0;
break;
}
object = object->next;
}
#undef object_list
#undef object_str
#undef object_cycle
#undef object_check
}
/***********************************************************************************
* Procedure: match_opt
* Synopsis: rc = match_opt(optstr, argv, buf, exact)
* Purpose: Compare a string to see if it matches a given option
* If a successful match, return the number of argv entries used.
* If exact is specified, do not look for extension strings.
***********************************************************************************/
int match_opt(char *optstr,
char **argv,
char *buf,
int exact
)
{
char *p;
int len, olen;
p = strchr(optstr, '%');
if ((p == NULL) || (buf == NULL))
{
/* We have just a plain option. For it to match, we must have */
/* a complete match on the characters. */
return(!strcmp(optstr, *argv));
}
if (exact) return 0; /* not looking for options strings this pass */
/* Not a plain one. There are a couple of possibilities: */
/* -opt%s No space allowed after the option */
/* -opt %s Space after option is optional */
/* -opt %s Space after option is mandatory. */
olen = len = p - optstr;
while(len && (optstr[len-1] == ' ')) len--;
if (memcmp(optstr, *argv, len))
return(0);
/* We know that at least we match the start of the option. Now we */
/* Have to get the buffer filled in. */
if ((*argv)[len]) /* We know that the parameter is with this option */
{
/* If the space is mandatory, we obviously didn't make a match */
if ((olen - len) > 1) return(0);
strncpy(buf, argv[0]+len, MAX_STRING);
return(1);
}
/* Parameter is with the next option */
/* If the space is not allowed, we didn't get a match */
if ((olen == len) || (argv[1] == NULL)) return(0);
strncpy(buf, argv[1], MAX_STRING);
return(2);
}
/***********************************************************************************
* Procedure: append_option
* Synopsis: rc = append_option(optstr, buffer, length)
* Purpose: append option to string buffer, update count and pointer
***********************************************************************************/
int append_option(char *optstr1, char *optstr2, char **buf, int *len)
{
int l;
char *p, *optr;
optr = optstr1; /* unless we change it to fetch a string */
p = strchr(optstr1, '%');
if ((p != NULL) && (optstr2 != NULL)) /* seems we have a string */
{
l = p - optstr1;
if (l >= *len) return 0; /*no room to save it */
if (optstr1[l-1] == ' ')
l -= 1;
memcpy(*buf, optstr1, l);
*buf += l;
*len -= l;
**buf = '\0'; /* just in case next step fails... */
optr = optstr2;
}
/* now copy whatever is left and put a blank on the end */
l = strlen(optr);
if (l >= *len) return 0; /* no room to save it */
strcpy(*buf, optr);
*buf += l;
strcpy(*buf, " ");
*buf += 1;
*len -= (l + 1);
return 1;
}
/***********************************************************************************
* Procedure: get_option
* Synopsis: rc = get_option(object, buffer, length)
* Purpose: make a string of all options that are set
***********************************************************************************/
int get_option(struct G_OBJECT *object,
char **buf,
int *len
)
{
#define object_list ((struct G_LIST *)object)
#define object_str ((struct G_STRING *)object)
#define object_check ((struct G_CHECK *)object)
#define object_cycle ((struct G_CYCLE *)object)
while (object != NULL)
{
switch (object->class)
{
case CLASS_CYCLE:
{
struct G_VALUE *val;
val = object_cycle->curval;
if (val->option[0] == 0) break; /* option not set */
if
(
append_option(val->option,
val->string != NULL ? val->string->buf : NULL,
buf, len)
== 0
)
return 0; /* no room to save it */
break;
}
case CLASS_CHECK:
{
char *p;
p = object->state ? object_check->option1 : object_check->option0;
if (*p) /* do we gots a string? */
if (append_option(p, NULL, buf, len) == 0)
return 0; /* shucks, nowhere to put it */
break;
}
case CLASS_STRING:
{
if (object_str->buf[0] == 0) break; /* empty string */
if
(
append_option(object_str->option,
object_str->buf,
buf, len)
== 0
)
return 0; /* no room, buf is full... */
break;
}
case CLASS_LIST:
{
struct G_ENTRY *ent;
ent = object_list->first;
while (ent != NULL) /* empty list will fall through */
{
if
(append_option(object_list->option, ent->buf, buf, len) == 0)
return 0; /* seem familiar? */
ent = (struct G_ENTRY *)ent->base.next;
}
break;
}
}
object = object->next;
}
return 1;
#undef object_check
#undef object_list
#undef object_str
#undef object_cycle
}
/***********************************************************************************
* Procedure: set_option
* Synopsis: rc = set_option(object, optstr, exact)
* Purpose: Set an option based on a option string, allowing parms if not exact
***********************************************************************************/
int set_option(struct G_OBJECT *object,
char **argv,
int exact
)
{
int used;
#define object_list ((struct G_LIST *)object)
#define object_str ((struct G_STRING *)object)
#define object_check ((struct G_CHECK *)object)
#define object_cycle ((struct G_CYCLE *)object)
used = 0;
while((used == 0) && (object != NULL))
{
switch(object->class)
{
case CLASS_STRING:
used = match_opt(object_str->option, argv, object_str->buf, exact);
break;
case CLASS_CYCLE:
{
struct G_VALUE *val;
for(val = object_cycle->values;
val != NULL;
val = (struct G_VALUE *)val->next)
{
used = match_opt(val->option, argv,
val->string ? val->string->buf : NULL,
exact);
if (used)
{
object_cycle->curval = val;
break;
}
}
}
break;
case CLASS_CHECK:
used = match_opt(object_check->option0, argv, NULL, exact);
if (used)
{
object->state = 0;
}
else
{
used = match_opt(object_check->option1, argv, NULL, exact);
if (used)
{
object->state = 1;
}
}
break;
case CLASS_LIST:
{
char buf[MAX_STRING+1];
used = match_opt(object_list->option, argv, buf, exact);
if (used)
{
struct G_ENTRY *ent, *prevent;
ent = get_mem(sizeof(struct G_ENTRY));
if (ent)
{
strcpy(ent->buf, buf);
if ((prevent = object_list->top) == NULL)
object_list->top = object_list->first = ent;
else
{
while(prevent->base.next)
prevent = (struct G_ENTRY *)prevent->base.next;
prevent->base.next = (struct G_OBJECT *)ent;
ent->base.prev = (struct G_OBJECT *)prevent;
}
}
}
}
break;
default:
object->state = 0;
break;
}
object = object->next;
}
return(used);
#undef object_check
#undef object_list
#undef object_str
#undef object_cycle
}
/***********************************************************************************
* Procedure: parse_options
* Synopsis: parse_options(string);
* Purpose: Parse an option and set the appropriate options in the global options
* data. This subroutine destroys the string buffer.
***********************************************************************************/
#define MAX_BUF 100
int parse_options(char *string)
{
#define MAX_ARGS 80
/* First we need to create an ARGV type array */
/* We will assume no more than 80 arguments in a line */
char *argv[MAX_ARGS];
int argc;
int state;
/*
* Character Classes:
* SQ ' Single Quote
* DQ " Double Quote
* BL Space, Tab White Space
* NL Null End of the string
* OT Other Anything else
* SQ DQ BL NL OT
* 0 1 2 0 x 3
* 1 >0 +1 +1 >x +1
* 2 +2 >0 +2 >x +2
* 3 +3 +3 >0 >x +3
*/
#define SQ 0
#define DQ 1
#define BL 2
#define NL 3
#define OT 4
#define EXI 15
#define STATE_MASK 0x0f
#define ACTION_SHIFT 4
#define ACT_SKP 1
#define ACT_OUT 2
#define ACT_BEG 3
#define SKP (ACT_SKP << ACTION_SHIFT)
#define OUT (ACT_OUT << ACTION_SHIFT)
#define BEG (ACT_BEG << ACTION_SHIFT)
static char class_tab[][5] = {
{ SKP|1, SKP|2, 0, EXI, BEG|3 },
{ OUT|0, 1, 1, OUT|EXI, 1 },
{ 2, OUT|0, 2, OUT|EXI, 2 },
{ 3, 3, OUT|0, OUT|EXI, 3 }
};
argc = 0;
state = 0;
while(state != EXI)
{
int class;
int look;
switch(*string)
{
case '\'': class = SQ; break;
case '"' : class = DQ; break;
case ' ' :
case '\t': class = BL; break;
case 0: class = NL; break;
default: class = OT; break;
}
look = class_tab[state][class];
state = look & STATE_MASK;
switch(look >> ACTION_SHIFT)
{
case ACT_SKP:
argv[argc++] = string+1;
break;
case ACT_OUT:
*string = 0;
break;
case ACT_BEG:
argv[argc++] = string;
break;
}
string++;
}
if (argc == 0) return(0);
argv[argc] = NULL;
{
int i, used, exact;
struct G_GROUP *group;
for(i = 0; i < argc; i += used)
{
used = 0; /* we've used what we used before */
/* do exact matches first to find things like -I0 */
for (exact = 1; (exact >= 0) && (used == 0); exact -= 1)
{
used = set_option(global.objects, argv+i, exact);
for(group = global.groups; (used == 0) && (group != NULL);
group = (struct G_GROUP *)group->base.next)
{
used = set_option(group->objects, argv+i, exact);
}
}
if ((!used) && (exact <= 0))
{
/* The option wasn't found, tell them in some way */
if (!request(0, TEXT_BADOPT, argv[i], NULL))
return(1);
else
used = 1; /* throw away the token and continue */
}
}
}
return(0);
}